﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Mogre;

namespace WastedMechanics
{
    public abstract class AnimBlender
    {
        public AnimComposition Target;
        public Dictionary<String, AnimStatePart> AnimStates;
        public Dictionary<String, AnimComposition> Anims;
        public float Speed;
        public float TimeScale;
        public Character Character;
        public Dictionary<string, AnimGroup> Groups;
        public List<List<AnimStatePart>> LinkedStates;

        public AnimBlender(Character character)
        {
            Character = character;
            Groups = new Dictionary<string, AnimGroup>();
            Anims = new Dictionary<string, AnimComposition>();
            LinkedStates = new List<List<AnimStatePart>>();
            AnimStates = new Dictionary<string, AnimStatePart>();
            character.BodyEntity.Skeleton.BlendMode = SkeletonAnimationBlendMode.ANIMBLEND_CUMULATIVE;            

            CreateGroups();
            CreateAnimStates();
            LinkAnimations();
            ComposeAnimations();
                        
            Speed = 0.2f;
            TimeScale = 1.0f;
        }

        protected abstract void LinkAnimations();
        protected abstract void CreateAnimStates();
        protected abstract void ComposeAnimations();
        protected abstract void CreateGroups();

        public void AddAnimState(Entity entity, string name, bool loop = true)
        {
            AnimStates.Add(name, new AnimStatePart(entity.GetAnimationState(name)));            
            AnimStates[name].Loop = loop;
            AnimStates[name].Enabled = false;
        }

        public void ReloadParams()
        {
            Character.BodyEntity.Skeleton.BlendMode = SkeletonAnimationBlendMode.ANIMBLEND_CUMULATIVE; 
            foreach (AnimStatePart part in AnimStates.Values)
            {                
                part.AnimationState = Character.BodyEntity.GetAnimationState(part.Name);                
                part.RestoreParams(); 
            }
            foreach (AnimGroup group in Groups.Values)
            {
                group.ResetBlendMasks();
            }
        }

        public void SaveStatesParams()
        {
            foreach (AnimStatePart part in AnimStates.Values)
                part.SaveParams();
        }
            
        public void Update()
        {            
            foreach (AnimGroup group in Groups.Values)
            {
                float activeCount = 0;
                foreach (AnimStatePart state in group.AnimStates)
                {    
                    if (state.Weight > 0 && (Target == null || !Target.AnimStates.Contains(state)))
                    {
                        activeCount++;
                        state.Weight -= Speed / state.AffectedGroupsCount;
                        if (state.Weight <= 0.0f)
                        {
                            state.Weight = 0;
                            state.Enabled = false;
                        }
                    }
                    if (state.Enabled)                                            
                        state.AddTime(((TimeScale * 1.0f / state.AffectedGroupsCount) / (Engine.FixedFPS * 1.5f)));                    
                }
                if (Target != null)
                {
                    Target.Enable();
                    foreach (AnimStatePart state in Target.AnimStates)
                    {
                        if (group.AnimStates.Contains(state))
                        {
                            if (state.Weight < 1.0f)
                                state.Weight = Helper.Clamp(0, 1, state.Weight + (Speed * activeCount) / state.AffectedGroupsCount);
                        }
                    }
                }
            }

            foreach (List<AnimStatePart> linked in LinkedStates)
            {
                foreach (AnimStatePart state in linked)
                    state.TimePosition = linked[0].TimePosition;
            }
        }

        public void SetAnimationInstantly(string name)
        {
            Target = Anims[name];
            foreach (AnimStatePart state in AnimStates.Values)
            {  
                state.Weight = 0;
                state.Enabled = false;                
            }
            Target.Enable();
            foreach (AnimStatePart state in Target.AnimStates)
                state.Weight = 1;
        }
    }
}
